#include "config.h"

#include <stdio.h>

#include "ecm.h"

#include "license.h"

char	*edbd_clxdir;

extern char *ecm_encrypt(const char *str, ecm_size_t len);

/*
 * keygen program main -------------------------------------------------
 *
 * If this file is compiled with -DKEYGEN, the license key generator
 * is created. (should be compiled with base32.c)
 */

static void
usage(char *prog_name)
{
	fprintf(stderr, "License key generator for ECM\n");
	fprintf(stderr, "Usage: %s <arch> <expire_time> <node_limit> [<prefix>]\n",
					prog_name);
	fprintf(stderr, "       %s -d <license_key>\n", prog_name);
	fprintf(stderr, "  <arch>        : \"x\" for any, \"i\" for i386 "
					"or \"a\" for alpha/ia64\n");
	fprintf(stderr, "  <expire_time> : YYYY.MM.DD.hh:mm format expire time "
					"or \"unlimited\"\n");
	fprintf(stderr, "  <node_limit>  : number of nodes or \"unlimited\"\n");
	fprintf(stderr, "  <prefix>      : \"ECM10\""
					"(default: \"ECM10\")\n");
}

static int encodeMap[32] = {
	/* A - Z */
	65, 66, 67, 68, 69, 70, 71, 72,  /* A, B, C, D, E, F, G, H */
	74, 75, 76, 77, 79, 80,  /* J, K, L, M, O, P */
	81, 82, 83, 84, 86, 87, 89, 90,  /* Q, R, S, T, V, W, Y, Z */
	/* 0 - 9 */
	48, 49, 50, 51, 52, 53, 54, 55, 56, 57
};

static char *
base32_encode(char *_org, int org_len, int *enc_len)
{
	unsigned char *org, *enc, *e;
	int i, x1, x2, n;

	/* returns dynamically allocated buffer */

	/* Check if valid input array */
	if (_org == NULL || org_len < 0 || org_len % 5) {
		*enc_len = 0;
		return NULL;
	}

	org = (unsigned char *)_org;
	n = org_len;
	*enc_len = ((n + 2) / 5) * 8;
	enc = (unsigned char *)ecm_malloc(*enc_len);

	e = enc;
	for (i = 0; i < n; i += 5) {
		if (n - i >= 5) {
			x1 = ((int)org[i] << 16) + ((int)org[i+1] << 8) + ((int)org[i+2]);
			e[0] = (unsigned char)encodeMap[(x1 >> 19) & 0x1f];
			e[1] = (unsigned char)encodeMap[(x1 >> 14) & 0x1f];
			e[2] = (unsigned char)encodeMap[(x1 >>  9) & 0x1f];
			e[3] = (unsigned char)encodeMap[(x1 >>  4) & 0x1f];

			x2 = ((int)org[i+2] << 16) + ((int)org[i+3] << 8) + ((int)org[i+4]);
			e[4] = (unsigned char)encodeMap[(x2 >> 15) & 0x1f];
			e[5] = (unsigned char)encodeMap[(x2 >> 10) & 0x1f];
			e[6] = (unsigned char)encodeMap[(x2 >>  5) & 0x1f];
			e[7] = (unsigned char)encodeMap[(x2) & 0x1f];
		}
		e += 8;
	}

	return (char *)enc;
} 

static ECMBOOL
product_key_to_license_key(char *product_key, char *license_key_buf,
			   char *license_key_prefix)
{
	char raw_key[15], encoded_key[31];
	char *encrypted_key, *base32_encoded_key;
	int i, j, license_key_checksum, raw_key_checksum;

	if (product_key == NULL)
		return ECM_FALSE;
	if (license_key_prefix == NULL)
		return ECM_FALSE;
	if (strlen(license_key_prefix) != 5)
		return ECM_FALSE;  /* invalid prefix length */

	/* get raw_key */
	memcpy(raw_key, product_key, 14);
	raw_key_checksum = 0;
	for (i = 0; i < 14; i++)
		raw_key_checksum += (unsigned char)raw_key[i];
	raw_key[14] = (raw_key_checksum * LICENSE_KEY_C2) % 256;
	
	/* get encrypted_key */
	encrypted_key = ecm_encrypt(raw_key, 15);
	if (encrypted_key == NULL)
		return ECM_FALSE;  /* encryption error */

	/* get base32_encoded_key */
	base32_encoded_key = base32_encode(encrypted_key, 15, &i);
	ecm_free(encrypted_key);
	if (base32_encoded_key == NULL)
		return ECM_FALSE;  /* encoding error */
	if (i != 24) {
		ecm_free(base32_encoded_key);
		return ECM_FALSE;  /* encoding error */
	}

	/* get encoded key */
	memcpy(&encoded_key[0], license_key_prefix, 5);
	strncpy(&encoded_key[5], base32_encoded_key, 24);
	ecm_free(base32_encoded_key);
	license_key_checksum = '-' * 5;
	for (i = 0; i < 29; i++)
		license_key_checksum += (unsigned char)encoded_key[i];
	sprintf(&encoded_key[29], "%X", (license_key_checksum * LICENSE_KEY_C1) % 16);

	/* copy license_key_buf */
	j = 0;
	for (i = 0; encoded_key[i] != 0; i++) {
		license_key_buf[j] = encoded_key[i];
		j++; 
		if (i % 5 == 4) {
			license_key_buf[j] = '-';
			j++;
		}
	}
	license_key_buf[35] = 0;  

	return ECM_TRUE;
}

static ECMBOOL
get_license_key(char *prefix, int random_factor, int product_arch,
		int expire_time, int node_limit, char *license_key_buf)
{
	product_key_t pk;

	/* get product_key */
	memcpy(pk.product_symbol, prefix, 5);
	pk.product_arch = product_arch;
	if (expire_time == 0) {
		pk.expire_time = (random_factor / 60) * 60 + 1;
		pk.random_key = random_factor % 60;
	}
	else {
		pk.expire_time = (expire_time / 60) * 60;
		pk.random_key = random_factor % 30000;
	}
	pk.node_limit = node_limit;  

	/* shift fields */
	pk.node_limit = SHIFT_SHORT(pk.node_limit, NODE_LIMIT_SHIFT);
	pk.expire_time = SHIFT_INT(pk.expire_time, EXPIRE_TIME_SHIFT);

	/* change to network byte order */
	pk.expire_time = ecm_htonl(pk.expire_time);
	pk.node_limit = ecm_htons(pk.node_limit);
	pk.random_key = ecm_htons(pk.random_key);

	/* suffle bits */
	
#define SWAP_BIT(aB, ab, bB, bb)  \
	swap_bit((char *)&pk, ((aB) << 3) + (ab), ((bB) << 3) + (bb))

	SWAP_BIT(0, 1, 12, 7);
	SWAP_BIT(0, 3, 12, 4);
	SWAP_BIT(0, 5, 12, 2);
	SWAP_BIT(0, 6, 13, 0);
	SWAP_BIT(1, 0, 13, 3);
	SWAP_BIT(1, 3, 9,  2);
	SWAP_BIT(1, 6, 10, 5);
	SWAP_BIT(2, 1, 12, 1);
	SWAP_BIT(2, 3, 12, 5);
	SWAP_BIT(2, 4, 12, 6);
	SWAP_BIT(2, 7, 12, 3);
	SWAP_BIT(3, 0, 13, 1);
	SWAP_BIT(3, 2, 13, 2);
	SWAP_BIT(4, 5, 13, 7);
	SWAP_BIT(4, 6, 13, 6);
	SWAP_BIT(5, 7, 13, 5);

	return product_key_to_license_key((char *)&pk, license_key_buf, prefix);
}

static char *
create_license_key(char *prefix, char *arch_str,
		   char *expire_time_str, char *node_limit_str)
{
	int	arch, expire_time, node_limit, random_factor; 
	char	key[256];

	/* check architecture type */
	if (strcmp(arch_str, "x") != 0 &&
	    strcmp(arch_str, "i") != 0 &&
	    strcmp(arch_str, "a") != 0)
		return NULL;

	arch = arch_str[0];

	/* check expire time */
	if (!strcmp(expire_time_str, "unlimited"))
		expire_time = 0;
	else {
		struct tm	t;

		if (sscanf(expire_time_str, "%d.%d.%d.%d:%d",
			   &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min) != 5)
			return NULL;

		t.tm_year = t.tm_year - 1900;
		t.tm_mon = t.tm_mon - 1;
		t.tm_sec = 0;
		t.tm_isdst = 0;

		expire_time = (int)mktime(&t);
		if (expire_time == -1)
			return NULL;   
	} 

	/* check node_limit */
	if (strcmp(node_limit_str, "unlimited") == 0)
		node_limit = 0;
	else if (sscanf(node_limit_str, "%d", &node_limit) != 1)
		return NULL;

	/* check prefix */
	if (prefix == NULL)
		prefix = "ECM20";

	/* get random factor */
	random_factor =  (int)((ecm_tickcount() * 0x5deece66dLL + 0xb + ecm_tickcount() * 167) & 0x7fffffff);

	/* get key and print out */
	if (!get_license_key(prefix, random_factor,
			     arch, expire_time, node_limit, key))
		return NULL;
	return ecm_strdup(key);
}

static int
license_get_info(char *key, char *info_ret)
{
	int arch, expire_time, node_limit;
	char arch_str[64], expire_time_str[64], node_limit_str[64];

	if (key == NULL || info_ret == NULL)
		return -1;

	if (!get_license_value(key, &arch, &expire_time, &node_limit))
		return -1;

	get_license_arch_str(arch, arch_str);

	if (expire_time == 0)
		strcpy(expire_time_str, "unlimited");
	else {
		struct tm *t;
		time_t tc; 
		tc = (time_t)expire_time;
		t = localtime(&tc);
		sprintf(expire_time_str, "%d.%d.%d.%d:%d",
				t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
				t->tm_hour, t->tm_min);

		/* old style */
		/*
		sprintf(expire_time_str, ctime((const time_t *)&expire_time));
		expire_time_str[strlen(expire_time_str) - 1] = 0;
		*/
	}

	if (node_limit == 0)
		strcpy(node_limit_str, "unlimited");
	else
		sprintf(node_limit_str, "%d", node_limit);

	sprintf(info_ret, "%s level, %s arch, %s nodes, until %s\n",
		get_license_level_str(get_license_level(key)),
		arch_str, node_limit_str, expire_time_str);

	return 0;
}

int
main(int argc, char **argv)
{
	ecm_initialize();

	if (argc == 4 || argc == 5) {
		/* generate license key */
		char	*key;
		key = create_license_key(argv[4], argv[1], argv[2], argv[3]);
		if (key == NULL) {
			fprintf(stderr, "invalid input paramaters\n");
			return 1;
		}
		else {
			printf("%s\n", key);
			ecm_free(key);
			return 0;
		}
	}
	else if (argc == 3) {  /* parse license key */
		if (!strcmp(argv[1], "-d")) {
			char info[256];

			if (license_get_info(argv[2], info) == 0) {
				printf("ECM license key for %s", info);
				return 0;
			}
		}
		fprintf(stderr, "invalid license key\n");
	}

	usage(argv[0]);
	return 1;
}
